import fs from "fs-extra";
import path from "path";
import { Ancestor, IAdditionalMsg } from "./Ancestor.js";
import { toolchain } from "./toolchain.js";
import { checkOrLockWorkspace, I18NLock } from "./tools/lock.js";
import { SSHClient } from "./tools/SSHClient.js";
import { SVNHelper } from "./tools/SVNHelper.js";
import { Hudson } from "./tools/hudson.js";
import axios from "axios";
import { env } from "./env.js";
import { Cmd } from "./tools/Cmd.js";
import { emphasize, sendRobotMsg } from "./tools/alert.js";
import { PlatformTypes } from "./typings.js";
import { download, getHudsonLogURL, joinURLs } from "./tools/vendor.js";

export class Servant extends Ancestor {
    private conclusion: string = '';
    private additionalMessages: IAdditionalMsg[] = [];

    public async awake(): Promise<boolean> {
        return true;
    }

    public async start(): Promise<void> {
        if (this.cmdOption.task === 'makeI18nXlsx') {
            await this.makeI18nXlsx();
        } else if (this.cmdOption.task === 'PullPublish') {
            await this.pullPublish();
        } else if (this.cmdOption.task === 'SetTiShen') {
            await this.setTiShen();
        } else if (this.cmdOption.task === 'UpdateTiShenIOS') {
            // 斗破ios的提审方式
            await this.UpdateTiShenIOS();
        } else if (this.cmdOption.task === 'SwitchPublishfix') {
            await this.switchPublishfix();
        } else if (this.cmdOption.task === 'DetectProxy') {
            await this.detectProxy();
        } else {
            console.error(`Not support task: ${this.cmdOption.task}`);
            process.exit(1);
        }
    }

    private async makeI18nXlsx(): Promise<void> {
        const [projectName, platform] = this.cmdOption.taskParameter!.split(',');
        this.cmdOption.projectName = projectName;
        this.cmdOption.platform = platform as PlatformTypes;

        // 更新ini配置
        if (env.projIniRoot && fs.existsSync(env.projIniRoot)) {
            await toolchain.svnClient.cleanup(false, env.projIniRoot);
            await toolchain.svnClient.update(env.projIniRoot);
        }
        
        await this.getParams();

        // 检查构建锁
        await checkOrLockWorkspace(I18NLock);

        await this.cleanProj(toolchain.params.workSpacePath, toolchain.params.svn.urlMap.code[toolchain.params.projectType]);
        
        await toolchain.platformCfgParser.getChannelCfg(this.cmdOption, toolchain.params);    
        console.log('channelcfg:', toolchain.params.channelCfg);

        if (!toolchain.params.channelCfg.localize) {
            console.error('未在cfg.json中配置localize');
            process.exit(1);
        }
        await toolchain.i18n.prepareI18n();

        const out = await toolchain.i18n.runI18N(this.cmdOption, 'S');
        if (out != null) {
            if (out.autoTransFailedCnt > 0) {
                this.additionalMessages.push({
                    type: 'markdown',
                    msg: `${out.autoTransFailedCnt}条文字在线翻译失败，具体请查看日志：[构建日志](${getHudsonLogURL()})`
                }, {
                    type: 'text',
                    mention: await Hudson.getStartUser(),
                    msg: '请进行人工翻译'
                });
            }
            if (out.report && out.report.noTranslators?.length > 0) {
                this.additionalMessages.push({
                    type: 'text',
                    msg: '以下UI未挂载Translator脚本，将导致UI上的静态文本无法切换到其他语言：' + out.report.noTranslators.join(', ')
                })
            }
            if (out.newOutput) {
                const relout = path.relative(toolchain.params.workSpacePath, out.outputRoot);
                if (relout.includes(path.sep)) {
                    await toolchain.svnClient.add(path.join(toolchain.params.workSpacePath, relout.substring(0, relout.indexOf(path.sep))));
                } else {
                    await toolchain.svnClient.add(out.outputRoot);
                }
            }
            await toolchain.svnClient.addUnversioned(out.outputRoot);
            await toolchain.svnClient.commit('更新翻译--来自构建机', out.outputRoot);
        }

        this.conclusion = '参数：' + this.cmdOption.taskParameter;
    }

    private async pullPublish(): Promise<void> {        
        await this.getParams();
        await SVNHelper.pullPublish();
        this.additionalMessages.push({ type: 'text', msg: `拉分支已完成，[点击查看日志](${Hudson.getLogUrl()})` });
    }

    private async setTiShen(): Promise<void> {
        interface ITiShen {
            version: 2
            isTiShen: boolean
            appVer: string
            tiShenAppVers: string[]
            svr: string
        }

        const [status, tishenVer, upload] = this.cmdOption.taskParameter!.split(';');
        if (status != '0' && status != '1') {
            console.error('参数错误！', this.cmdOption.taskParameter);
            process.exit(1);
        }
        let appVer = tishenVer.trim();
        if (this.cmdOption.webglRuntime != 'douyin') {
            const mch = tishenVer.match(/([\.\d]+)\((\d+)\)/);
            if (mch != null) {
                // 不需要括号内的资源版本号
                appVer = mch[1];
            }
        }
        
        await this.getParams();
        await toolchain.platformCfgParser.getChannelCfg(this.cmdOption, toolchain.params);

        const channelCfg = toolchain.params.channelCfg;
        console.log('channelcfg:', channelCfg);
        if (channelCfg.tishen_svr == null) {
            console.error('cfg.json需配置提审信息');
            process.exit(1);
        }
        
        await toolchain.hostInfoParser.getBuildCfg(this.cmdOption, toolchain.params);

        // 拉取cdn上最新的配置
        const subDir = toolchain.unity.getAssetsDir();
        const tsFileName = `tishen_${channelCfg.gameid}.txt`;

        const url = joinURLs(channelCfg.url, subDir, tsFileName);
        const latestOutput = path.join(toolchain.params.workSpacePath, `../.build/tishen_${channelCfg.gameid}@latest.txt`);
        const dlres = await download(url, latestOutput);

        // 获得当前处于提审状态的版本号
        let tsVers: string[] = [];
        if (dlres == 0 && fs.existsSync(latestOutput)) {
            const latestCfg: ITiShen = await fs.readJson(latestOutput);
            console.log('latest tishen config online: ', latestCfg);
            if (latestCfg.version > 1) {
                // v2版本
                tsVers = latestCfg.tiShenAppVers;
            } else {
                if (latestCfg.isTiShen) {
                    tsVers.push(latestCfg.appVer);
                }
            }
        }

        if (status === '1') {
            if (!tsVers.includes(appVer)) {
                tsVers.push(appVer);
            }
        } else {
            // 处理v2版本字段
            if (appVer && !appVer.startsWith('0.0.0.0')) {
                const idx = tsVers.indexOf(appVer);
                if (idx >= 0) {
                    tsVers.splice(idx, 1);
                } else {
                    console.error('no tishen app version canceled, maybe the input version is not correct.');
                }
            } else {
                // 取消所有版本的提审状态
                tsVers.length = 0;
            }
        }

        // app_ver,ip,port,server_id        
        const info: ITiShen = { version: 2, isTiShen: false, appVer: '', svr: channelCfg.tishen_svr.host.replace(':', ',') + ',' + channelCfg.tishen_svr.id, tiShenAppVers: [] };
        // 先处理v1版本字段，v1版本通过isTiShen和appVer指定唯一一个版本处于提审状态
        if (status === '1') {
            // 保留v1的字段以兼容旧程序
            info.isTiShen = true;
            info.appVer = appVer;
        }
        // 处理v2版本字段
        info.tiShenAppVers = tsVers;

        if (upload != '0') {
            const cdn = toolchain.params.cdnHostInfo;
            const assetsDir = `${cdn.cdnDir}/${subDir}`;
            
            const ssh = new SSHClient();
            await ssh.connect(cdn.hostinfo);
            await ssh.exec(`mkdir -p ${assetsDir}`);
            await ssh.exec(`echo -e '${JSON.stringify(info, null, 2)}' >${assetsDir}/${tsFileName}`);
            await ssh.end();
        } else {
            await fs.writeFile(path.join(toolchain.params.uploadPath, subDir, tsFileName), JSON.stringify(info, null, 2), 'utf-8');
        }


        const platName = toolchain.option.webglRuntime == 'minigame' ? '小游戏' : '抖音';
        const tsVersStr = tsVers.map(v => emphasize(v)).join(', ');
        
        if (status === '1') {
            this.conclusion = `打上提审标记的版本号：${emphasize(appVer)}`;
            if (tsVers.length > 1) {
                this.additionalMessages.push({
                    type: 'markdown',
                    msg: `${emphasize(platName)}当前有${tsVers.length}个版本被打上提审标记，分别为：${tsVersStr}`
                });
            }
            if (toolchain.option.webglRuntime == 'minigame') {
                this.additionalMessages.push({
                    type: 'markdown',
                    msg: `${emphasize('小游戏')}提审步骤二：前往[发布更新](http://builder.fygame.com:3000/#/pages/tools/publish)，注意判断是否只更新${emphasize('提审服')}`
                });
            }
        } else {
            if (tsVers.length == 0) {
                this.conclusion = '已取消所有版本的提审标记';
            } else {
                this.conclusion = `取消提审标记的版本号：${emphasize(appVer)}`;
            }
            if (tsVers.length > 0) {
                this.additionalMessages.push({
                    type: 'markdown',
                    msg: `${emphasize(platName)}当前剩余${tsVers.length}个版本被打上提审标记，分别为：${tsVersStr}`
                });
            }
            if (toolchain.option.webglRuntime == 'minigame') {
                this.additionalMessages.push({
                    type: 'markdown',
                    msg: `${emphasize('小游戏')}提审状态已取消，请前往[发布更新](http://builder.fygame.com:3000/#/pages/tools/publish)进行常规更新`
                });
            } else if (toolchain.option.webglRuntime == 'douyin') {
                this.additionalMessages.push({
                    type: 'markdown',
                    msg: `${emphasize('抖音')}提审状态已取消`
                });
            }
        }
    }

    private async UpdateTiShenIOS(): Promise<void> {        
        await this.getParams();
        // 仅更新cfg.json所在目录
        const cfgRoot = path.join(toolchain.params.workSpacePath, toolchain.params.platformPath);
        await toolchain.svnClient.update(cfgRoot);

        await toolchain.platformCfgParser.getChannelCfg(this.cmdOption, toolchain.params);

        const cfgs = toolchain.platformCfgParser.getAll()!;
        let t = '';
        for (const cfg of cfgs) {
            if (cfg.tiShen?.ts) {
                t += `${cfg.bundleVersion},${cfg.tiShen.serverIp},${cfg.tiShen.serverPort},${cfg.tiShen.serverIndex},id${cfg.gameid};`;
            }
        }

        const tsConfig = JSON.stringify({ t });
        console.log('set ts.json:', tsConfig);
        
        // 写入res目录
        const subDir = toolchain.unity.getAssetsDir();
        const localJson = path.join(toolchain.params.uploadPath, subDir, 'ts.json');
        await fs.writeFile(localJson, tsConfig, 'utf-8');
        // 同时生成一份.txt的，防止运维json文件老是有缓存
        const localTxt = path.join(toolchain.params.uploadPath, subDir, 'ts.txt');
        await fs.writeFile(localTxt, tsConfig, 'utf-8');
        
        // 上传到cdn
        await toolchain.hostInfoParser.getBuildCfg(this.cmdOption, toolchain.params);
        const cdn = toolchain.params.cdnHostInfo;

        const assetsDir = `${cdn.cdnDir}/${subDir}`;
        
        const ssh = new SSHClient();
        await ssh.connect(cdn.hostinfo);
        await ssh.exec(`mkdir -p ${assetsDir}`);
        await ssh.exec(`echo -e '${tsConfig}' >${assetsDir}/ts.json`);
        await ssh.exec(`echo -e '${tsConfig}' >${assetsDir}/ts.txt`);
        await ssh.end();

        this.conclusion = `提审配置：${tsConfig}`;
    }

    private async switchPublishfix(): Promise<void> {
        await this.getParams();
        let rootpath = toolchain.params.workSpacePath;
        console.log("rootpath: " + rootpath);

        console.log("@@ copy publish@ver to publishfix");
        try {
            await toolchain.svnClient.delete(toolchain.params.svn.urlMap.code.publishfix, { message: 'auto by builder' });
        } catch(e) {
            if (!String(e).includes("E160013")) throw e;
        }
        const tover = await axios.get<string>(this.cmdOption.taskParameter! + "/svnver.txt", { timeout: 10000 });
        await toolchain.svnClient.copy(toolchain.params.svn.urlMap.code.publish + `@${tover.data}` , toolchain.params.svn.urlMap.code.publishfix, { message: `auto by builder copy from publish@${tover.data}` });

        try {
            await toolchain.svnClient.delete(toolchain.params.svn.urlMap.xls.publishfix, { message: 'auto by builder' });
        } catch(e) {
            if (!String(e).includes("E160013")) throw e;
        }
        const xlstover = await axios.get<string>(this.cmdOption.taskParameter! + "/xlssvnver.txt", { timeout: 10000 });
        await toolchain.svnClient.copy(toolchain.params.svn.urlMap.xls.publish + `@${xlstover.data}` , toolchain.params.svn.urlMap.xls.publishfix, { message: `auto by builder copy from publish@${xlstover.data}` });

        await this.cleanProj(toolchain.params.workSpacePath, toolchain.params.svn.urlMap.code.publishfix);
    }

    private async detectProxy(): Promise<void> {
        if (!env.clientGroupChatID) return;
        const cmd = new Cmd();
        const out = await cmd.run('curl', ['https://www.google.com', '-x', 'http://127.0.0.1:10809'], { silent: true }).catch((e) => {
            console.error(e);
        });
        if (out != 0) {
            console.error('curl failed: ' + cmd.output);
            await sendRobotMsg('text', '请勿关闭构建机上的v2ray', env.clientGroupMention, env.clientGroupChatID);
        }
    }

    public async getBuildConclusion(): Promise<string> {
        return this.conclusion;
    }

    public async getAdditionalMessages(): Promise<IAdditionalMsg[]> {
        return this.additionalMessages;
    }
}